<?php
namespace ScarecrowCache\lib\core;

use ScarecrowCache\lib\interfaces\DbHandleAbstract;

class DbHanlde extends DbHandleAbstract {

	public function __construct($isDebug = false)
	{
		parent::__construct($isDebug);
		$this->openDbFile();
	}

	/**
	 * 删除一个KEY
	 * @param string $key
	 * @return int
	 */
	public function delete(string $key)
	{
		// TODO: Implement delete() method.
		$offect = $this->hashMap($key) % S_DB_BUCKET_SIZE * 4;
		fseek($this->dbIdxHandle, $offect);
		$pos = unpack('L', fread($this->dbIdxHandle, 4))[1];
		$curr = $pos;
		$prev = 0;
		$isFound = false;
		while ($curr) {
			fseek($this->dbIdxHandle, $curr);
			$block = fread($this->dbIdxHandle, S_DB_INDEX_SIZE);
			$next = unpack('L', substr($block, 0, 4))[1];
			$cpkey = substr($block, 4, S_DB_KEY_SIZE);
			if (!strncmp($key, $cpkey, strlen($key))) {
				$isFound= true;
				break;
			}

			$prev = $curr;
			$curr = $next;
		}

		if (!$isFound) {
			return S_DB_FAIL;
		}

		if ($prev == 0) {
			fseek($this->dbIdxHandle, $offect);
		} else {
			fseek($this->dbIdxHandle, $prev);
		}
		fwrite($this->dbIdxHandle, pack('L', $next), 4);
		return S_DB_SUCCESS;

	}

	/**
	 * 查找一个KEY
	 * @param string $key
	 * @param null $default
	 * @return bool|null|string
	 */
	public function fetch(string $key, $default=null)
	{
		// TODO: Implement fetch() method.
		$offect = $this->hashMap($key) % S_DB_BUCKET_SIZE * 4;

		fseek($this->dbIdxHandle, $offect, SEEK_SET);
		$pos = unpack('L', fread($this->dbIdxHandle, 4))[1];

		$isFound = false;
		while ($pos) {
			fseek($this->dbIdxHandle, $pos);
			$block = fread($this->dbIdxHandle, S_DB_INDEX_SIZE);
			$cpkey = substr($block, 4, S_DB_KEY_SIZE);
			if (!strncmp($key, $cpkey, strlen($key))) {
				$dataOff = unpack('L', substr($block, S_DB_KEY_SIZE+4,4))[1];
				$dataLen = unpack('L', substr($block, S_DB_KEY_SIZE+8,4))[1];
				$isFound = true;
				break;
			}
			$pos = unpack('L', substr($block,0,4))[1];
		}
		if (!$isFound) {
			return $default;
		} else {
			fseek($this->dbDataHandle, $dataOff);
			$data = fread($this->dbDataHandle, $dataLen);
			return $data;
		}
	}

	/**
	 * 插入一个KEY
	 * @param string $key
	 * @param $value
	 * @param int $flag
	 * @return int
	 */
	public function insert(string $key, $value, $flag = S_DB_STORE)
	{
		// TODO: Implement insert() method.
		$offset = $this->hashMap($key) % S_DB_BUCKET_SIZE * 4;
		$idxOff = (int)fstat($this->dbIdxHandle)['size'];
		$dataOff = (int)fstat($this->dbDataHandle)['size'];

		$keyLen = strlen($key);
		if ($keyLen > S_DB_KEY_SIZE) {
			return S_DB_FAIL;
		}

		$bemp = pack('L', 0x00000000);
		$bstr = $bemp . $key;
		$space = S_DB_KEY_SIZE - $keyLen;
		for ($i=0; $i < $space; $i++) {
			$bstr .= pack('C', 0x00);
		}
		$bstr .= pack('L', $dataOff);
		$bstr .= pack('L', strlen($value));

		fseek($this->dbIdxHandle, $offset, SEEK_SET);
		$pos = unpack('L', fread($this->dbIdxHandle, 4))[1];

		if ($pos == 0) {
			fseek($this->dbIdxHandle, $offset, SEEK_SET);
			fwrite($this->dbIdxHandle, pack('L', $idxOff), 4);
			fseek($this->dbIdxHandle, 0, SEEK_END);
			fwrite($this->dbIdxHandle, $bstr, S_DB_INDEX_SIZE);
			fseek($this->dbDataHandle, 0, SEEK_END);
			fwrite($this->dbDataHandle, $value, strlen($value));
			return S_DB_SUCCESS;
		}

		while ($pos) {
			fseek($this->dbIdxHandle,$pos,SEEK_SET);
			$tempBlock = fread($this->dbIdxHandle, S_DB_INDEX_SIZE);
			$cpKey = substr($tempBlock,4 , S_DB_KEY_SIZE);
			if (!strncmp($key, $cpKey, $keyLen)) {
				if ($flag == S_DB_INSERT) {
					return S_DB_KEY_EXISTS;
				}
				if ($flag == S_DB_STORE) {
					$offset = $pos+4+S_DB_KEY_SIZE;
					fseek($this->dbIdxHandle, $offset, SEEK_SET);
					fwrite($this->dbIdxHandle, pack('L', $dataOff), 4);
					fwrite($this->dbIdxHandle, pack('L', strlen($value)), 4);
					fseek($this->dbDataHandle, 0, SEEK_END);
					fwrite($this->dbDataHandle, $value, strlen($value));
				}
				return S_DB_SUCCESS;
			}
			$prev = $pos;
			$pos = unpack('L', substr($tempBlock,0,4))[1];
		}

		fseek($this->dbIdxHandle, $prev, SEEK_SET);
		fwrite($this->dbIdxHandle, pack('L', $idxOff), 4);
		fseek($this->dbIdxHandle, 0, SEEK_END);
		fwrite($this->dbIdxHandle, $bstr, S_DB_INDEX_SIZE);
		fseek($this->dbDataHandle, 0, SEEK_END);
		fwrite($this->dbDataHandle, $value, strlen($value));
		return S_DB_SUCCESS;
	}
}